package com.whz.generic.uuid;

import com.google.common.collect.Lists;

import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * 随机密码生成器,密码支持含有 [小写字母,大写字母,数字,特殊字符]四种符号类型
 *
 * @author whz
 */
public class PasswordGenerator {
    /**
     * 特殊字符
     */
    public static final char[] ALLOWED_SPECIAL_CHARACTERS = {
            '`', '~', '@', '#', '$', '%', '^', '&',
            '*', '(', ')', '-', '_', '=', '+', '[',
            '{', '}', ']', '\\', '|', ';', ':', '"',
            '\'', ',', '<', '.', '>', '/', '?'
    };


    /**
     * 26个字符
     */
    private static final int LETTER_RANGE = 26;
    /**
     * 10个数字
     */
    private static final int NUMBER_RANGE = 10;
    /**
     * 特殊字符数量
     */
    private static final int SP_CHARACTER_RANGE = ALLOWED_SPECIAL_CHARACTERS.length;
    /**
     * 随机生成器
     */
    private static final Random RANDOM = new SecureRandom();
    /**
     * 密码长度
     */
    private final int passwordLength;
    /**
     * 密码包含的最少符号种类数量
     */
    private final int minVariousType;

    /**
     * 创建随机密码生成器,指定密码长度passwordLength,最少包含的符号种类数量minVariousType
     *
     * @param passwordLength 密码长度
     * @param minVariousType 密码包含的最少符号种类数量
     */
    public PasswordGenerator(int passwordLength, int minVariousType) {
        if (minVariousType > CharactersType.values().length) {
            minVariousType = CharactersType.values().length;
        }
        if (minVariousType > passwordLength) {
            minVariousType = passwordLength;
        }
        this.passwordLength = passwordLength;
        this.minVariousType = minVariousType;
    }

    public String generateRandomPassword() {
        char[] password = new char[passwordLength];
        List<Integer> pwCharsIndex = Lists.newArrayListWithCapacity(passwordLength);
        for (int i = 0; i < password.length; i++) {
            pwCharsIndex.add(i);
        }
        List<CharactersType> takeTypes = Lists.newArrayList(CharactersType.values());
        List<CharactersType> fixedTypes = Arrays.asList(CharactersType.values());
        int typeCount = 0;
        while (!pwCharsIndex.isEmpty()) {
            //随机填充一位密码
            int pwIndex = pwCharsIndex.remove(RANDOM.nextInt(pwCharsIndex.size()));
            Character c;
            if (typeCount < minVariousType) {
                //最少包含的不同种类字符
                c = generateCharacter(takeTypes.remove(RANDOM.nextInt(takeTypes.size())));
                typeCount++;
            } else {
                //随机生成所有种类密码
                c = generateCharacter(fixedTypes.get(RANDOM.nextInt(fixedTypes.size())));
            }
            password[pwIndex] = c;
        }
        return String.valueOf(password);
    }

    private static Character generateCharacter(CharactersType type) {
        Character c = null;
        int rand;
        switch (type) {
        case LOWERCASE:
            /*
             * 小写字母
             */
            rand = RANDOM.nextInt(LETTER_RANGE);
            rand += 97;
            c = (char) rand;
            break;
        case UPPERCASE:
            /*
             * 大写字母
             */
            rand = RANDOM.nextInt(LETTER_RANGE);
            rand += 65;
            c = (char) rand;
            break;
        case NUMBER:
            /*
             *数字
             */
            rand = RANDOM.nextInt(NUMBER_RANGE);
            rand += 48;
            c = (char) rand;
            break;
        case SPECIAL_CHARACTER:
            /*
             *特殊字符
             */
            rand = RANDOM.nextInt(SP_CHARACTER_RANGE);
            c = ALLOWED_SPECIAL_CHARACTERS[rand];
            break;
        default:
        }
        return c;
    }
}